package com.rongji.dfish.misc.docpreview.parser;

import com.rongji.dfish.base.util.LogUtil;
import com.rongji.dfish.base.util.StringUtil;
import com.rongji.dfish.misc.docpreview.DocumentParser;
import com.rongji.dfish.misc.docpreview.data.*;
import com.rongji.dfish.misc.docpreview.data.Document;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.*;

/**
 * word 03版本解析器 后缀名是.docx
 */
public class DocxParser extends DocumentParser {
    @Override
    public Document parse(InputStream is) {
        XWPFDocument xwpfDocument = null;
        List<DocumentElement> paragraphContents = new ArrayList<>();
        try {
            xwpfDocument = new XWPFDocument(is);
        } catch (IOException e) {
            LogUtil.error("", e);
        }
        initCache(xwpfDocument);

        Iterator<IBodyElement> iter= xwpfDocument.getBodyElementsIterator();
        while (iter.hasNext()) {
            IBodyElement ibe=iter.next();
            if(ibe instanceof XWPFParagraph ){
                XWPFParagraph para = (XWPFParagraph)ibe;
                parseParagraph(para,paragraphContents);
            }else if(ibe instanceof XWPFTable){
                XWPFTable tbl = (XWPFTable)ibe;
                paragraphContents.add(parseTabel(tbl));
            }

        }


        Document doc=new Document();
        doc.setBody(paragraphContents);
        doc.originDoc(xwpfDocument);
        return doc;
    }

    private Table parseTabel(XWPFTable tbl) {
        Table table=new Table();
        //columns

        table.setRows(new ArrayList<TableRow>() );
        for( XWPFTableRow r:tbl.getRows()){
            TableRow row=new TableRow();
            table.getRows().add(row);
            row.setCells(new ArrayList<TableCell>());
            for(XWPFTableCell tc:r.getTableCells()){
                TableCell cell=new TableCell();
                Iterator<IBodyElement> iter= tc.getBodyElements().iterator();
                List<DocumentElement> body=new ArrayList<>();
                cell.setBody(body);
                while (iter.hasNext()) {
                    IBodyElement ibe=iter.next();
                    if(ibe instanceof XWPFParagraph ){
                        XWPFParagraph para = (XWPFParagraph)ibe;
                        parseParagraph(para,body);
                    }else if(ibe instanceof XWPFTable){
                        XWPFTable subTbl = (XWPFTable)ibe;
                        body.add(parseTabel(subTbl));
                    }

                }

                row.getCells().add(cell);
            }
        }

        return table;
    }

    private void parseParagraph(XWPFParagraph para, List<DocumentElement> container) {


        List<XWPFRun> runsLists = para.getRuns();
        // 段元素
        Paragraph p = new Paragraph();


        String numberLabel=null;
        CTPPr ppr=para.getCTP().getPPr();
        if(ppr!=null) {
            //尝试读取 分页符号
            CTOnOff onOff=ppr.getPageBreakBefore();
            if(onOff!=null&&
                    (!onOff.isSetVal()||onOff.getVal().equals(STOnOff.X_1)) ){
                //分页
                container.add(new PageBreaker());
            }
            //尝试读取 多级编号
            fillMultiNumber(para,p);
//            ppr.getPStyle().getVal();
//            para.getDocument().getStyle().getStyleList()

//            CTNumPr numPr=ppr.getNumPr();
//            if(numPr!=null) {
//
//                numberLabel="ilvl="+numPr.getIlvl().getVal()+"\tnumId="+numPr.getNumId().getVal();
//            }
        }

        // 一行可以有多个元素
        for (XWPFRun run : runsLists) {
            CharacterRun cr = new CharacterRun();
            cr.setBold(run.isBold());
            cr.setColor(run.getColor()==null?null:("#" + run.getColor()));
            cr.setItalic(run.isItalic());

            cr.setText(run.getText(0));
            cr.setFontSize(run.getFontSize()==-1?null:run.getFontSize());
            cr.setFontFamily(run.getFontFamily());
            if (run.getUnderline() != UnderlinePatterns.NONE) {// 下划线代号
                cr.setStrikeType(CharacterRun.STRIKE_UNDERLINE);
            } else if (run.isStrike()) {
                cr.setStrikeType(CharacterRun.STRIKE_LINE_THROUGH);
            }


            List <XWPFPicture>  pics=run .getEmbeddedPictures();
            if(pics!=null&&pics.size()>0){
                int picIndex=0;
                for(XWPFPicture pic:pics){
                    if (pic.getPictureData() != null) {
                        byte[] data=pic.getPictureData().getData();
                        String ext=pic.getPictureData().suggestFileExtension();

                        /*
                        读取 图片宽度高度。 这里EMU转化为像素用 6691 来除可能有一定误差
                         */
                        Drawing drawing=new Drawing();
                        if(run.getCTR()!=null&&run.getCTR().getDrawingList()!=null&&
                                run.getCTR().getDrawingList().size()>picIndex){
                            CTDrawing  ctDrawing= run.getCTR().getDrawingList().get(picIndex);
                            if(ctDrawing!=null&&ctDrawing.getInlineList()!=null&&ctDrawing.getInlineList().size()>0){
                                CTInline inline=ctDrawing.getInlineList().get(0);
                                if(inline!=null){
                                    CTPositiveSize2D size=inline.getExtent();
                                    if(size!=null){
                                        if(size.getCx()>0){
                                            drawing.setPicWidth((int)size.getCx()/6691);
                                        }
                                        if(size.getCy()>0){
                                            drawing.setPicHeight((int)size.getCy()/6691);
                                        }
                                    }
                                }
                            }
                        }

                        savePic(data,ext,drawing);
                        p.getBody().add(drawing);
                    }
                    picIndex++;
                }
                if(cr.getText() !=null&&!"".equals(cr.getText().trim())){
                    p.getBody().add(cr);
                }
            }else{
                p.getBody().add(cr);
            }


            // CTDrawing
            // CTInline
            // CTPositiveSize2D
            //

            //run.addPicture(null,2,"filename.png",400*9144,300*9144);
        }
        if(numberLabel!=null){
            if(p.getBody()!=null&&p.getBody().size()>0){
                String str=numberLabel+" ";
                for(ParagraphElement pe:p.getBody()){
                    if(pe instanceof CharacterRun){
                        str+=((CharacterRun) pe).getText();
                    }
                }
                System.out.println(str);
            }
        }
        p.setIndentation(para.getIndentationFirstLine() == -1 ? null : para.getIndentationFirstLine() );
        p.setAlignment(para.getAlignment().name());//FIXME 总是LEFT ?
        container.add(p);
    }

    /**
     * 尝试读取多级编号，并增加一个CharacterRun
     * 多级编号可能来自Style中(格式刷) 或者来自段落自己的numPr
     * @param para
     * @param p
     */
    private void fillMultiNumber(XWPFParagraph para, Paragraph p) {
        CTPPr ppr=para.getCTP().getPPr();
        CTStyles styles=null;
        try {
            styles=para.getDocument().getStyle();
        }catch (Exception ex){
            LogUtil.error(null,ex);
        }

        if(ppr!=null) {
            CTNumPr numPr=ppr.getNumPr();
            if(numPr==null) {
                String style=ppr.getPStyle()==null?null:ppr.getPStyle().getVal();
                CTStyle theStyle=null;
                if(style!=null&& styles!=null&&styles.getStyleList()!=null){
                    for(CTStyle ctStyle:styles.getStyleList()){
                        if(ctStyle.getStyleId().equals(style)){
                            theStyle=ctStyle;
                            break;
                        }
                    }
                }
                if(theStyle!=null&&theStyle.getPPr()!=null){
                    numPr=theStyle.getPPr().getNumPr();

                }
            }

            if(numPr!=null) {
                XWPFNum num=getNum(numPr.getNumId().getVal());
                if (num!=null&& num.getCTNum()!=null&&
                        num.getCTNum().getAbstractNumId()!=null) {
                    XWPFAbstractNum absNum = getAbstractNum(num.getCTNum().getAbstractNumId().getVal());
                    assert absNum != null;
                    //如果是multilevel 则增加一个 数字
//                    if (absNum.getCTAbstractNum().isSetMultiLevelType()) {
                    BigInteger ival=numPr.getIlvl()==null?new BigInteger("0"):numPr.getIlvl().getVal();
                    p.getBody().add(generateNumber(absNum.getCTAbstractNum(),p,ival));
                    //取得这个结果
//                    }
                }
                //System.out.println("【格式刷】ilvl="+numPr.getIlvl().getVal()+"\tnumId="+numPr.getNumId().getVal());
            }
        }

    }

    private CharacterRun generateNumber(CTAbstractNum ctAbstractNum,Paragraph para, BigInteger bi) {
        CharacterRun cr=new CharacterRun();

        int lvl=bi.intValue();
        String abstractNumId=ctAbstractNum.getAbstractNumId().toString();
        int[] cachedNum=numberCache.get(abstractNumId);
        if(cachedNum==null){
            cachedNum=new int[10];
            initCachedNum(cachedNum,ctAbstractNum,0);//初始化的时候按start-1 初始化
            numberCache.put(abstractNumId,cachedNum);
        }

        int v=cachedNum[lvl];
        v++;
        cachedNum[lvl]=v; //进行编号的时候加1 就直接是编号值 。 这样递归到下级的时候不会乱。
        initCachedNum(cachedNum,ctAbstractNum,lvl+1);

        CTLvl ctLvl=ctAbstractNum.getLvlList().get(lvl);

        CTNumFmt numFmt=ctLvl.getNumFmt();
        if(numFmt.getVal().equals(STNumberFormat.DECIMAL)) {
            String text = ctLvl.getLvlText().getVal();
            text = replaceNumText(text, cachedNum);
            cr.setText(text);
        }else if(numFmt.getVal().equals(STNumberFormat.CHINESE_COUNTING)||
                numFmt.getVal().equals(STNumberFormat.CHINESE_COUNTING_THOUSAND)) {
            String text = ctLvl.getLvlText().getVal();
            text = replaceChineseNumText(text, cachedNum);
            cr.setText(text);
        }else if(numFmt.getVal().equals(STNumberFormat.BULLET)) {
            cr.setText("●");
        }
        if(ctLvl.getLvlJc()!=null){
            STJc.Enum jc=ctLvl.getLvlJc().getVal();
            para.setAlignment(jc.toString());
        }


//        cr.setText("这是是多级标签位 ilvl="+bi);
        return cr;
    }

    XWPFNumbering numbering;
    HashMap<BigInteger,XWPFNum>numMap=new HashMap<>();
    HashMap<BigInteger,XWPFAbstractNum>absNumMap=new HashMap<>();
    private void initCache(XWPFDocument doc){
        numbering=doc.getNumbering();
    }
    private XWPFNum getNum(BigInteger numId){
        if(numId==null){
            numId=new BigInteger("0");
        }
        return numMap.computeIfAbsent(numId, key ->numbering.getNum(key));
//        return numbering.getNum(bi);
    }
    private XWPFAbstractNum getAbstractNum(BigInteger abstractNumID){
        if(abstractNumID==null){
            abstractNumID=new BigInteger("0");
        }
        return absNumMap.computeIfAbsent(abstractNumID, key ->numbering.getAbstractNum(key));
//        return numbering.getAbstractNum(bi);
    }

    private String replaceNumText(String text, int[] cachedNum) {
        for(int i=0;i<10;i++){
            text=text.replace("%"+(i+1),String.valueOf(cachedNum[i]));
        }
        return text;
    }
    private String replaceChineseNumText(String text, int[] cachedNum) {
        for(int i=0;i<10;i++){
            text=text.replace("%"+(i+1),StringUtil.intToChinese(cachedNum[i]));
        }
        return text;
    }

    private void initCachedNum(int[] cachedNum, CTAbstractNum ctAbstractNum, int begin) {
        //将编号重置
        for(int i=begin;i<ctAbstractNum.getLvlList().size();i++){

            CTLvl ctLvl=ctAbstractNum.getLvlList().get(i);
            cachedNum[i]=ctLvl.getStart().getVal().intValue()-1;
        }
    }

    Map<String,int[]> numberCache=new HashMap<>();


}
